const Joi = require("@hapi/joi")

/** 
 * @file stmtBuilder.js 
 * @description 该文件提供了一系列用于构建不同类型SQL语句的函数，支持MySQL、sqlite和MSSQL数据库。 
 * 包括列表查询、分页查询、更新、插入以及根据条件选择更新或插入的语句构建功能。 
 * 同时还包含了一些数据加密和转换的辅助函数。
 * @author ZouAnZu
 
 const sqlsConfig={
    user_info:{
     apiName:'用户信息',
      dbDrive: 'mysql',
      tableName: 'users',
      field:'*',
      paramsMapping:[],
      sort: []
    },
    user_info_search:{
      apiName:'用户信息查找',
      dbDrive: 'mysql',
      tableName: 'users',
      field:'*',
      conditionSchema:{
          keyword:{fields:['username','pwd'],operator:'like'}
      },
      paramsMapping:[{field:'keyword',alias:'keyword',source:'body'}],//alias作用，当body里的参数名与field不同时，用alias调整,可以隐藏真实字段名
      sort: []
    },
    user_modi:{
      apiName:'用户信息修改',
      dbDrive:'mysql',
      tableName: 'users',
      mutableFields:['username','pwd'],//要修改或插入数据的字段
      pk:'id',//主键，单挑记录修改或返回单条记录插入的自增id值，批量修改时，本条为空
      conditionSchema:{keyword:{fields:['username','pwd'],operator:'like'},stat:{fields:'stat',operator:'=',default:0}},//修改语句的条件，keyword：取值的参数名，应用于批量修改，当希望修改，新增共用本条设置设为空。
      paramsMapping:[{field:'id',source:'body',schema:Joi.number().min(1)},{field:'username',source:'body',alias:'keyword',schema:Joi.string().min(4).max(10).required()},{field:'pwd',alias:'password',source:'body'}],//从约定的途径获取用户上传的参数
      presetParams:{'stat':2}//预置参数
    }
  }
  */
class QueryBuilder {
/** 
 * @function buildListStmt 
 * @description 构建查询列表的SQL语句 
 * @param {object} config - 配置对象，包含查询相关的配置信息 
 * @param {object} parmas - 参数对象，包含查询条件参数的值 
 * @returns {object} 包含构建好的查询SQL语句、查询参数
 */ 
    static buildListStmt(config, params) {
        const fields=config?.field ?? '*'
        const dbDrive=config?.dbDrive ?? 'mysql',sort=config?.sort ?? []
        const args = dbDrive=='mssql'?{}:[]
        const where=this.buildCondition(config,params,args)
        const sql = `select ${fields}  from ${config?.tableName} ${where!==''?'where':''} ${where} ${this.buildSort(sort)}`
        return  {sql, args}
    }
     
  /** 
 * @function buildPageStmt 
 * @description 构建分页查询的SQL语句 
 * @param {object} config - 配置对象，包含查询相关的配置信息 
 * @param {object} parmas - 参数对象，包含查询条件和分页参数的值 
 * @returns {object} 包含构建好的总记录数查询SQL语句、总记录数查询参数、分页查询SQL语句和分页查询参数的对象 
 */ 
  static buildPageStmt(config, parmas) {
    const sort=config?.sort ?? []
    const dbDrive=config?.dbDrive ?? 'mysql'
    const sortStr=buildSort(sort)
    const args = dbDrive=='mssql'?{}:[]
    const where=this.buildCondition(config,params,args)
    let pagesize=parseInt(parmas.page_size),curpage=parseInt(parmas.current_page)
    pagesize=isNaN(pagesize)?10:pagesize
    curpage=isNaN(curpage)?1:curpage
    return {
        sql1:`select count(*) as total from ${config.tableName} ${where!==''?'where':''} ${where}` , 
        args1:(dbDrive=='mssql')?{}:[...args], 
        sql2:(dbDrive=='mssql')?
        `select * from (select ${config.field},ROW_NUMBER() OVER (${sortStr.trim() || 'order by id'}) AS RowNum , COUNT(*) OVER () AS TotalCount from ${config.tableName} ${where!==''?'where':''} ${where} ) as subQuery where RowNum between @start_row and @end_row`
        :
        `select ${config.field} from ${config.tableName} ${where!==''?'where':''} ${where}  ${sortStr} limit ? offset ?`, 
        args2:(dbDrive=='mssql')?{...args,...{start_row:pagesize*(curpage-1)+1,end_row:pagesize*curpage}}:[...args,...[pagesize,curpage]]
        }
    
    }
    /** 
 * @function buildModiStmt 
 * @description 根据配置对象和参数对象中主键是否存在及是否有值来判断具体构建插入语句还是更新语句，存在主键但没有值或存在主键且mutableFields包含主键且参数对象能取主键值，构建insert，其它构建update 
 * @param {object} config - 配置对象，包含相关的配置信息 
 * @param {object} param - 参数对象，包含插入的参数值 
 * @returns {object} 包含构建好的插入SQL语句和参数的对象 
 */ 
    static buildModiStmt(config,arg) {
        const pk=config?.pk ?? ''
            if ((pk!=='' && !arg[pk]) || (pk!=='' && config.mutableFields.includes(pk) && arg[pk])) return buildInsertStmt(config,arg)
            else return buildUpdateStmt(config,arg)
        }
/** 
 * @function buildUpdateStmt 
 * @description 构建更新操作的SQL语句 
 * @param {object} config - 配置对象，包含更新相关的配置信息 
 * @param {object} arg - 参数对象，包含更新的参数值 
 * @returns {object} 包含构建好的更新SQL语句和参数的对象 
 */   
    static buildUpdateStmt(config,arg){
        const dbDrive=config?.dbDrive,args=(dbDrive=='mssql')?{}:[]
        const pk=config?.pk ??'',mutableFields=config.mutableFields || []
        const temp=[]
        let where=''
        const params={...arg||{}}
        for (let field of mutableFields)
            if (params[field] ?? 'null' !=='null')
                if (dbDrive=='mssql'){
                    temp.push(`${field}=@${field}`)
                    args[field]=params[field]
                }else{
                    temp.push(`${field}=?`)
                    args.push(params[field])
                }
       
        if (pk !='' && params[pk] ){
            where=dbDrive=='mssql'?` ${pk}=@${pk}`:' ${pk}=?'
            dbDrive=='mssql'?args[pk]=params[pk]:args.push(params[pk])
        }
        else where=this.buildCondition(config,params,args)
        return {
            sql:`update ${config.tableName} set ${temp.join(',') } ${where!==''?'where':''} ${where}`,
            args
        }
    }


/** 
 * @function buildInsertStmt 
 * @description 构建插入操作的SQL语句 
 * @param {object} config - 配置对象，包含插入相关的配置信息 
 * @param {object} param - 参数对象，包含插入的参数值 
 * @returns {object} 包含构建好的插入SQL语句和参数的对象 
 */ 
    static buildInsertStmt(config,param) {
        const dbDrive=config?.dbDrive,args=(dbDrive=='mssql')?{}:[], pk=config?.pk ??''
        const temp1=[],temp2=[]
        const params={...param??{},...config?.presetParams??{}}
        const mutableFields=config?.mutableFields ??Object.keys(params)
        for (let field of mutableFields) 
            if (params[field] ?? 'null'!=='null' ){
                temp1.push(field)
                temp2.push((dbDrive=='mssql')?`@${field}`: '?')
                dbDrive=='mssql' ? args[field]=params[field]:args.push(params[field])
            } 

        const output=((dbDrive=='mssql') && pk!=='')?` OUTPUT INSERTED.${pk} as id`:''
        sql+=`${temp1})${output} values (${temp2})`
        return {
            sql:`insert ${config.tableName} (${temp1.join(',')})${output} values (${temp2.join(',')})`,
            args
            }
    }
    static buildCondition(config,params,args){
        const conditionSchema=config?.conditionSchema ?? {}
        const dbDrive=config?.dbDrive ?? 'mysql'
        const where=Object.entries(conditionSchema)
            .filter(([key])=>params[key]!==undefined)
            .map(([key,{fields,operator,preValue}])=>'('+this.buildSimpleCondition((!Array.isArray(fields))?[fields]:fields,preValue??params[key],args,operator,dbDrive)+')').join(' and ')
        return where
    }
/** 
 * @function buildSimpleCondition 
 * @description 构建简单的SQL条件语句 
 * @param {string[]} fields - 查询条件字段数组 
 * @param {any} val - 条件值 
 * @param {object|any[]} args - 用于存储参数的对象（MSSQL）或数组（MySQL） 
 * @param {string} operator - 操作符，如 'like'、'in'、'='、'>'、'<' 等 
 * @param {string} dbDrive - 数据库驱动，'mssql' 或'mysql' 
 * @returns {string} 构建好的SQL条件语句 
 */ 
    static buildSimpleCondition=(fields,val,args,operator,dbDrive)=>{
        const processedVal=(operator=='like')?`%${val}%`:(operator=='in')?` (${val}) `:val
        return fields.map(field=>{
            dbDrive=='mssql'?args[field+'_w']=processedVal:args.push(processedVal)
            return dbDrive=='mssql'?` ${field} ${operator} @${field}_w `:`  ${field} ${operator} ? `}).join(' OR')
    }
/**
 * @function buildSort
 * @description 构建SQL语句的排序子句
 * @param {object[]} sort  要求排序的配置项
 * @returns {string} 构建好的排序子句
 */
    static buildSort(sort){
        const temp= sort.map(({field,direction})=>`${field} ${direction??''}`).join(',')
        // for (let k of sort){
        //     temp+=`,${k['field']??''} ${k['direction']??''}`
        // }
        return temp==''?'':` order by ${temp}`
    }


}


// console.dir(QueryBuilder.buildUpdateStmt({
//     apiName:'用户信息修改',
//     dbDrive:'mysql',
//     tableName: 'users',
//     mutableFields:['username','pwd','stat'],//要修改或插入数据的字段
//     pk:'id',//主键，单挑记录修改或返回单条记录插入的自增id值，批量修改时，本条为空
//     conditionSchema:{keyword:{fields:['dep','com'],operator:'like'},stat:{fields:'stat',operator:'=',preValue:0}},//修改语句的条件，keyword：取值的参数名，应用于批量修改，当希望修改，新增共用本条设置设为空。
//     paramsMapping:[{field:'id',source:'body',schema:Joi.number().min(1)},{field:'username',source:'body',alias:'keyword',schema:Joi.string().min(4).max(10).required()},{field:'pwd',alias:'password',source:'body'}],//从约定的途径获取用户上传的参数
//     presetParams:{'stat':2}//预置参数
//   },{username:'zou',pwd:'123456',stat:1,keyword:'sales'}))
 


module.exports = exports={QueryBuilder}